package com.luo.comm.utils.mp;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.yulichang.query.MPJQueryWrapper;
import com.luo.comm.utils.MyBeanUtils;
import com.luo.comm.vo.BusinessException;
import com.luo.comm.vo.ConditionFields;
import com.luo.comm.vo.ReqBody;
import com.luo.comm.vo.MyResEnum;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;


import java.util.*;

public class MpUtils {

    /**
     * 保存前的预处理函数，对一些字段及字段条件进行转换处理。
     * @param object 入参是一个通用实体类，转换成jsonobject进行处理*/

    private static final String id = "id";
    private static final String createdBy = "createdBy";
    private static final String updatedBy = "updatedBy";
    private static final String createdAt = "createdAt";
    private static final String updatedAt = "updatedAt";
    private static final String deletedBy = "deletedBy";
    private static final String deletedAt = "deletedAt";


    public static void beforeSaveCommHandle(Object object)   {
        // 将baseEntity的属性补充到当前类，供后面使用
        Class<?> cls = object.getClass();
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(object));

        // 处理需要删除的字段。这些是不能指定添加的
        // removeList列表中字段都会被删除处理。约定这些是特殊字段，不能由用户填写
        List<String> removeList = Arrays.asList(id,createdBy,updatedBy,createdAt,updatedAt,deletedBy,deletedAt);
        for (String item: removeList ) {
            if(jsonObject.containsKey(item)){
                jsonObject.remove(item);
            }
        }

        // 如果一个属性都没有，禁止插入
        if(jsonObject.isEmpty()){
            throw BusinessException.error("save方法至少提供一个属性");
        }
        Object newObj = jsonObject.toJavaObject(cls);
        System.out.println("*************");
        // 处理结束后，将新实体类的属性再复制回原实体类，以便调用者可以直接用实体类获取新值。
        BeanUtils.copyProperties(newObj,object);
    }

    /**
     * 保存前的预处理函数，删除createdBy,createdAt,deletedBy,deletedAt字段等
     * @param object 入参是一个通用实体类，转换成jsonobject进行处理*/

    public static void beforeUpdateCommHandle(Object object) {

        Object obj = object;
        /** 将baseEntity的属性补充到当前类，供后面使用 */
        Class<?> cls = obj.getClass();
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(obj));

        /**检查是否存在id字段。不存在抛异常*/
        if(!jsonObject.containsKey(id) || ObjectUtils.isEmpty(jsonObject.get(id))){
            throw new BusinessException(MyResEnum.ID_EMPTY); // update方法调用的其实是updatedById，所以必须提供id。
        }

        /**检查是否存在乐观锁updateAt字段。不存在抛异常*/
        if(!jsonObject.containsKey(updatedAt) || ObjectUtils.isEmpty(jsonObject.get(updatedAt))){
            throw BusinessException.error("更新时必须提供updatedAt字段");
        }


        /** 处理需要删除的字段。这些是不能指定的 */
        // removeList列表中字段都会被删除处理。
        List<String> removeList = Arrays.asList(createdBy,createdAt,deletedBy,deletedAt);
        for (String item: removeList ) {
            if(jsonObject.containsKey(item)){
                jsonObject.remove(item);
            }
        }

        /** 删除公用字段外，除id和updatedAt字段以外，还需要包括至少一个字段，否则无更新内容*/
        if(jsonObject.keySet().stream().count()<3){
            throw new BusinessException(MyResEnum.UPDATE_NO_FIELDS);
        }

        Object newObj = jsonObject.toJavaObject(cls);

        // 处理结束后，将新实体类的属性再复制回原实体类，以便调用者可以直接用实体类获取新值。
        BeanUtils.copyProperties(newObj,object);
    }


    /** 获取实体类中id字段，如果不存在id则返回0;*/
    public static Long getEntityId(Object object) {
        /** 将baseEntity的属性补充到当前类，供后面使用 */
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(object));
        Long doId = Long.valueOf(0);

        /**检查是否存在id,如果id存在且不为null,则返回id，否则返回0*/
        if(jsonObject.containsKey(id) && null != jsonObject.get("id")){
            doId =Long.valueOf(String.valueOf(jsonObject.get("id")));
        }
        return doId;
    }

    /**
     * ConditionFields 的处理，处理模糊查询、范围字段查询等字段
     * 更多关于QueryWrapper参见myBatisPlus官网：https://baomidou.com/pages/10c804/#select
     * */
    public static void conditionOpCommHandle(ReqBody<?> reqbody, ConditionFields conditionFields, QueryWrapper<?> queryWrapper){

        Object obj = reqbody.getEntity();
        ConditionFields defaultConditionFields = new ConditionFields(); // 默认值

        // 将obj转为jsonObject，方便处理
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(obj));

        /* 遍历equalFieldsList，按eq添加到queryWrapper条件，并删除这个属性。需要最先处理！！！
         *  约定默认都是eq，所以如果是eq的是不用传值的。如果传了值，说明这个字段有被其他条件字段约束，这次仅是单个方法需要用精准查询，所以要先处理。
         * */
        ArrayList<String> equalFieldsList = conditionFields.getEqualFieldsList();
        if(null != defaultConditionFields.getEqualFieldsList()){
            // 如果有默认值，合并两个likeFieldsList,用这个最新的进行处理
            equalFieldsList.addAll(defaultConditionFields.getEqualFieldsList());
        }
        if(equalFieldsList !=null && !equalFieldsList.isEmpty()){
            for (String item: equalFieldsList ) {
                if(jsonObject.containsKey(item)){
                    // 字段格式统一由userName转为user_name格式，然后添加到queryWrapper条件，并从jsonObject移除。
                    queryWrapper.eq(MyBeanUtils.underscoreName(item),jsonObject.get(item));
                    jsonObject.remove(item);
                }
            }
        }

        
        // 遍历likeFieldsList，按like添加到queryWrapper条件，并删除这个属性
        ArrayList<String> likeFieldsList = conditionFields.getLikeFieldsList();
        if(null != defaultConditionFields.getLikeFieldsList()){
            // 如果有默认值，合并两个FieldsList,用这个最新的进行处理
            likeFieldsList.addAll(defaultConditionFields.getLikeFieldsList());
        }

        if(likeFieldsList !=null && !likeFieldsList.isEmpty()){
            for (String item: likeFieldsList ) {
                if(jsonObject.containsKey(item)){
                    // java字段都是userName这样命名格式的，统一转为user_name这样的数据库字段命名格式。
                    queryWrapper.like(MyBeanUtils.underscoreName(item),jsonObject.get(item));
                    jsonObject.remove(item);
                }
            }
        }

        // 将jsonObject中的between取出进行遍历，按between添加到queryWrapper条件，并删除这个属性
        // 日期字段要特别注意：数据库存的带时分秒的，如果入参不带时分秒进来默认就是0时0分0秒。需要+1或带23时59分59秒才能查截止当天。 */
        if(null != reqbody.getBetween()){
            // 如果包含该between，则进行处理
            LinkedList<Object> betweenList = reqbody.getBetween(); // betweenList = [{},{}]
            for (Object o : betweenList) {

                if (o.getClass() == LinkedHashMap.class) {
                    // 如果是LinkedHashMap类型的才处理。否则不处理
                    LinkedHashMap map =(LinkedHashMap) o;
                    Iterator it = map.entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry entry = (Map.Entry) it.next(); // entry  = LinkedHashMap
                        if (entry.getValue().getClass() == ArrayList.class) {
                            ArrayList val = (ArrayList) entry.getValue();
                            queryWrapper.between(MyBeanUtils.underscoreName(entry.getKey().toString()), val.get(0), val.get(1));
                        }
                    }
                }
            }

        }





        /**处理其他字段，参见ReqBody和ConditionFields这两个类，还有没有需要处理的字段，比如inFieldsList、inSql*/

        /**处理select字段*/
        if(null !=reqbody.getSelect()){
            queryWrapper.select(MyBeanUtils.underscoreName(reqbody.getSelect()));
            jsonObject.remove("select");
        }

        /**最后余下的字段，都按eq处理*/
        for (String key: jsonObject.keySet()){
            // java字段都是userName这样命名格式的，统一转为user_name这样的数据库字段命名格式。
            queryWrapper.eq(MyBeanUtils.underscoreName(key),jsonObject.get(key));
            jsonObject.remove(key);
        }


        /**处理orderBy字段*/
        LinkedList orderByList = reqbody.getOrderBy(); // orderByList = [{},{}]
        for (Integer i=0;i <orderByList.size();i++){

            if(orderByList.get(i).getClass() == new LinkedHashMap().getClass()){
                // 如果是LinkedHashMap类型的才处理。否则不处理
                LinkedHashMap map = (LinkedHashMap)orderByList.get(i);
                Iterator it = map.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry) it.next(); // entry  = LinkedHashMap
                    if("asc".equals(entry.getValue())){
                        queryWrapper.orderByAsc(MyBeanUtils.underscoreName(entry.getKey().toString()));
                    }else{
                        queryWrapper.orderByDesc(MyBeanUtils.underscoreName(entry.getKey().toString()));
                    }
                }
            }
        }

    }


    /**
     * MPJQueryWrapper ConditionFields 的处理，处理模糊查询、范围字段查询等字段
     *  除了入参不一样，内容和conditionOpCommHandle 一样
     * */
    public static void conditionMpjOpCommHandle(ReqBody<?> reqBody, ConditionFields conditionFields, MPJQueryWrapper queryWrapper){

        Object obj = reqBody.getEntity();
        ConditionFields defaultConditionFields = new ConditionFields(); // 默认值

        // 将obj转为jsonObject，方便处理
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(obj));

        /* 遍历equalFieldsList，按eq添加到queryWrapper条件，并删除这个属性。需要最先处理！！！
         *  约定默认都是eq，所以如果是eq的是不用传值的。如果传了值，说明这个字段有被其他条件字段约束，这次仅是单个方法需要用精准查询，所以要先处理。
         * */
        ArrayList<String> equalFieldsList = conditionFields.getEqualFieldsList();
        LinkedHashMap<String,Object> joinObj = conditionFields.getJoinObj();
        if(null != defaultConditionFields.getEqualFieldsList()){
            // 如果有默认值，合并两个likeFieldsList,用这个最新的进行处理
            equalFieldsList.addAll(defaultConditionFields.getEqualFieldsList());
        }
        if(equalFieldsList !=null && !equalFieldsList.isEmpty()){
            for (String item: equalFieldsList ) {
                if(jsonObject.containsKey(item)){
                    String newItem = getMpjObjAttr(item,joinObj);
                    // 字段格式统一由userName转为user_name格式，然后添加到queryWrapper条件，并从jsonObject移除。
                    queryWrapper.eq(MyBeanUtils.underscoreName(newItem),jsonObject.get(item));
                    jsonObject.remove(item);
                }
            }
        }


        // 遍历likeFieldsList，按like添加到queryWrapper条件，并删除这个属性
        ArrayList<String> likeFieldsList = conditionFields.getLikeFieldsList();
        if(null != defaultConditionFields.getLikeFieldsList()){
            // 如果有默认值，合并两个FieldsList,用这个最新的进行处理
            likeFieldsList.addAll(defaultConditionFields.getLikeFieldsList());
        }

        if(likeFieldsList !=null && !likeFieldsList.isEmpty()){
            for (String item: likeFieldsList ) {
                if(jsonObject.containsKey(item)){
                    String newItem = getMpjObjAttr(item,joinObj);
                    // java字段都是userName这样命名格式的，统一转为user_name这样的数据库字段命名格式。
                    queryWrapper.like(MyBeanUtils.underscoreName(newItem),jsonObject.get(item));
                    jsonObject.remove(item);
                }
            }
        }

        // 将jsonObject中的between取出进行遍历，按between添加到queryWrapper条件，并删除这个属性
        // 日期字段要特别注意：数据库存的带时分秒的，如果入参不带时分秒进来默认就是0时0分0秒。需要+1或带23时59分59秒才能查截止当天。 */
        if(null != reqBody.getBetween()){
            // 如果包含该between，则进行处理
            LinkedList<Object> betweenList = reqBody.getBetween(); // betweenList = [{},{}]
            for (Object o : betweenList) {

                if (o.getClass() == LinkedHashMap.class) {
                    // 如果是LinkedHashMap类型的才处理。否则不处理
                    LinkedHashMap map =(LinkedHashMap) o;
                    Iterator it = map.entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry entry = (Map.Entry) it.next(); // entry  = LinkedHashMap
                        if (entry.getValue().getClass() == ArrayList.class) {
                            ArrayList val = (ArrayList) entry.getValue();
                            String item = entry.getKey().toString();
                            String newItem = getMpjObjAttr(item,joinObj);
                            queryWrapper.between(MyBeanUtils.underscoreName(newItem), val.get(0), val.get(1));
                        }
                    }
                }
            }

        }





        /**处理其他字段，参见ReqBody和ConditionFields这两个类，还有没有需要处理的字段，比如inFieldsList、inSql*/

        /**处理select字段*/
        if(null !=reqBody.getSelect()){
            // 遍历处理，所以字段前加表名
            String[] selectArr = reqBody.getSelect().split(",");
            HashSet<String> joinedObj = new HashSet();
            for ( int i =0;i< selectArr.length;i++) {
                String attr = selectArr[i];  // id
                String newAttr = getMpjObjAttr(attr,joinObj);  // t.id  ui.email
                String[] newAttrArr = newAttr.split("\\.");   // [t,id]
                String joinObjName = newAttrArr[0];  // t
                if(!joinObjName.equals("t")){
                    // 如果joinedObj不存在，则将leftjoin添加到wrapper.
                    LinkedHashMap  tempJoinObj =(LinkedHashMap) joinObj.get(joinObjName);
                    if(!joinedObj.contains(joinObjName)){
                        joinedObj.add(joinObjName);
                        queryWrapper.leftJoin(tempJoinObj.get("leftJoin").toString()); // 可以在这儿更换为innerJoin.rightJoin等
                    }
                }

                // 修改原属性
                selectArr[i] = newAttr;
            }
            reqBody.setSelect(String.join(",", selectArr));
            queryWrapper.select(MyBeanUtils.underscoreName(reqBody.getSelect()));
            jsonObject.remove("select");
        }

        /**最后余下的字段，都按eq处理*/
        for (String key: jsonObject.keySet()){
            String newItem = getMpjObjAttr(key,joinObj);
            // java字段都是userName这样命名格式的，统一转为user_name这样的数据库字段命名格式。
            queryWrapper.eq(MyBeanUtils.underscoreName(newItem),jsonObject.get(key));
            jsonObject.remove(key);
        }


        /**处理orderBy字段*/
        LinkedList orderByList = reqBody.getOrderBy(); // orderByList = [{},{}]
        for (Integer i=0;i <orderByList.size();i++){

            if(orderByList.get(i).getClass() == new LinkedHashMap().getClass()){
                // 如果是LinkedHashMap类型的才处理。否则不处理
                LinkedHashMap map = (LinkedHashMap)orderByList.get(i);
                Iterator it = map.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry) it.next(); // entry  = LinkedHashMap
                    String newKey = getMpjObjAttr(entry.getKey().toString(),joinObj);
                    if("asc".equals(entry.getValue())){
                        queryWrapper.orderByAsc(MyBeanUtils.underscoreName(newKey));
                    }else{
                        queryWrapper.orderByDesc(MyBeanUtils.underscoreName(newKey));
                    }
                }
            }
        }

    }

    /**
     * getMpjObjAttr 转换为Mpj联查查询的字段，要转换为联表字段，比如id =>t.id。
     *  String newItem = getMpjObjAttr(item);
     * */
    public static String getMpjObjAttr(String rawAttribute, LinkedHashMap<String,Object> joinObj){

        String mpjObjAttr ="";
        // 遍历joinObj对象，进行逐个对象查找，如果找到了则设置 mpjObjAttr = ui.rawAttribute
        for (String key : joinObj.keySet()) {
            LinkedHashMap  tempJoinObj =(LinkedHashMap) joinObj.get(key);

            if (tempJoinObj.containsKey(rawAttribute)){
                mpjObjAttr = tempJoinObj.get(rawAttribute).toString();
            }
        }

        // 如果不在DTO属性列内，按普通的列表处理
        if(mpjObjAttr.isEmpty()){
            mpjObjAttr = "t."+rawAttribute;
        }
        return mpjObjAttr;
    }


}
